/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package matematikapirma.gui;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.util.LinkedList;
import javax.swing.JPanel;
import javax.swing.JToolTip;
import matematikapirma.Curve;
import matematikapirma.Point;
import matematikapirma.Surface;

public class GraphicEngine extends JPanel {

    private BufferedImage bi;
    private int colorNum = 0;
    private Graphics2D big;
    private Rectangle area;
    private MainRighClickMenu menu;
    private LinkedList<GuiPoint> guiMainPoints = new LinkedList<GuiPoint>();
    private LinkedList<MiniPoint> miniPoints = new LinkedList<MiniPoint>();
    double last_x, last_y;
    boolean pressOut = false;
    private GuiPoint movinShape;
    private PointMenu pointMenu;
    private boolean pressed = false;
    private Surface surface;
    private JToolTip tooltip = new JToolTip();
    private Window window;

    public GraphicEngine(Window window, Surface surface, MainRighClickMenu menu, PointMenu pointMenu) {


        this.menu = menu;
        this.pointMenu = pointMenu;
        this.surface = surface;
        this.window = window;

        setBackground(Color.white);
        bi = new BufferedImage(300, 399, BufferedImage.TYPE_INT_RGB);
        big = bi.createGraphics();


        MouseListener popupListener = new PopupListener();

        addMouseListener(popupListener);

        addMouseMotionListener(new PointControlMouseMotionListener());
        addMouseMotionListener(new InfoMenuListener());
        addMouseListener(new PointControlMouseListener());
    }

    public Surface getSurface() {
        return surface;
    }

    class PointControlMouseMotionListener implements MouseMotionListener {

        public void mouseDragged(MouseEvent e) {

            if (!pressOut && pressed) {
                updateLocation(e);
            } else {
            }
        }

        public void mouseMoved(MouseEvent e) {
            for (GuiPoint shape : guiMainPoints) {
                if (shape.contains(e.getX(), e.getY())) {

                    movinShape = shape;
                    if (shape.hasCurve()) {
                        surface.setCurrentCurve(shape.getCurve());
                    }
                    break;
                }
            }
        }
    }

    class PointControlMouseListener implements MouseListener {

        public void mousePressed(MouseEvent e) {
            if (e.isPopupTrigger()) {
                pressed = false;
                return;
            }

            if (movinShape == null) {
                pressed = false;
                return;
            }
            if (movinShape.contains(e.getX(), e.getY())) {
                pressed = true;
                last_x = movinShape.getX() - e.getX();
                last_y = movinShape.getY() - e.getY();
                updateLocation(e);
            } else {
                pressOut = true;
            }
        }

        public void mouseReleased(MouseEvent e) {
            if (movinShape == null) {
                return;
            }
            if (movinShape.contains(e.getX(), e.getY()) && pressed) {
                updateLocation(e);
            } else {
                pressOut = false;
            }
        }

        public void mouseClicked(MouseEvent e) {
        }

        public void mouseExited(MouseEvent e) {
        }

        public void mouseEntered(MouseEvent e) {
        }
    }

    class PopupListener extends MouseAdapter {

        @Override
        public void mousePressed(MouseEvent e) {
            if (!maybeShowPointMenu(e)) {
                maybeShowMainMenu(e);
            }
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            if (!maybeShowPointMenu(e)) {
                maybeShowMainMenu(e);
            }
        }

        private void maybeShowMainMenu(MouseEvent e) {
            if (e.isPopupTrigger()) {
                pressed = false;
                menu.show(e.getComponent(),
                        e.getX(), e.getY());
                menu.setDestCoords(e.getX(), e.getY());
            }
        }

        private boolean maybeShowPointMenu(MouseEvent e) {
            if (e.isPopupTrigger()) {
                pressed = false;
                GuiPoint myPoint = null;
                for (GuiPoint shape : guiMainPoints) {
                    if (shape.contains(e.getX(), e.getY())) {
                        myPoint = shape;
                        break;
                    }
                }
                if (myPoint == null) {
                    return false;
                }
                pointMenu.show(e.getComponent(),
                        e.getX(), e.getY());
                pointMenu.setDestCoords(e.getX(), e.getY());
                pointMenu.setTargetPoint(myPoint);
                pointMenu.updateLabels();
                return true;
            }
            return false;
        }
    }

    class InfoMenuListener implements MouseMotionListener {

        public void mouseDragged(MouseEvent e) {
            
        }

        public void mouseMoved(MouseEvent e) {
            LinkedList<GuiPoint> allPoints = new LinkedList<GuiPoint>();
            allPoints.addAll(miniPoints);
            allPoints.addAll(guiMainPoints);
            
            for(GuiPoint point : allPoints){
                if(point.contains(e.getX(), e.getY())){
                    window.getStatusBar().setText(point.getPoint().toString());
                    return;
                }
            }

            window.getStatusBar().setText(" ");

        }
        
    }

    public void updateLocation(MouseEvent e) {
        if (movinShape == null) {
            return;
        }
        //movinShape.setFrame(last_x + e.getX(), last_y + e.getY(), GuiPoint.SIZE, GuiPoint.SIZE);
        movinShape.getPoint().setX(last_x + e.getX() + GuiPoint.SIZE / 2);
        movinShape.getPoint().setY(last_y + e.getY()+ GuiPoint.SIZE / 2);
        getSurface().getCurrentCurve().updateCurve(movinShape.getPoint());
        repaint();
    }

    @Override
    public void paint(Graphics g) {
        RenderingHints rh = new RenderingHints(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        ((Graphics2D) g).setRenderingHints(rh);
        paintComponent(g);
    }

    protected Color getCurveColor(){
        Color [] colors = {Color.BLACK, Color.GREEN, Color.ORANGE, Color.MAGENTA, Color.PINK, Color.GRAY};
        if(colorNum == colors.length){
            colorNum  = 0;
        }
        return colors[colorNum++];
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D graphics2d = (Graphics2D) g;
        Dimension dim = getSize();

        int w = (int) dim.getWidth();
        int h = (int) dim.getHeight();
        graphics2d.setPaint(Color.white);
        graphics2d.fillRect(0, 0, w, h);

        guiMainPoints.clear();
        for (Curve curve : getSurface().getCurves()) {

            /**
             * Draw control points and lines
             */
            for (int i = 0; i < curve.getInitialPoints().size(); i++) {
                GuiPoint point = new GuiPoint(curve.getInitialPoints().get(i), curve);

                guiMainPoints.add(point);
                graphics2d.setPaint(Color.BLUE);
                BasicStroke stroke;
                if (curve == getSurface().getCurrentCurve()) {
                    stroke = new BasicStroke(2.0f);
                } else {
                    stroke = new BasicStroke(1.0f);
                }
                graphics2d.setStroke(stroke);
                graphics2d.draw(point);
                if (getSurface().getShowLines()) {
                    if (i + 1 < curve.getInitialPoints().size()) {
                        GuiPoint point2 = new GuiPoint(curve.getInitialPoints().get(i + 1), curve);

                        guiMainPoints.add(point2);
                        graphics2d.setPaint(Color.PINK);
                        if (curve == getSurface().getCurrentCurve()) {
                            stroke = new BasicStroke(1.5f);
                        } else {
                            stroke = new BasicStroke(0.7f);
                        }
                        graphics2d.setStroke(stroke);

                        graphics2d.drawLine((int) point.getCenterX(), (int) point.getCenterY(),
                                (int) point2.getCenterX(), (int) point2.getCenterY());
                    }
                }
            }

            /**
             * Draw curve control line (closed curve part)
             */
            if (!curve.isOpen() && getSurface().getShowLines()) {
                graphics2d.setPaint(Color.PINK);
                graphics2d.setStroke(new BasicStroke(0.7f));
                graphics2d.drawLine((int) curve.getInitialPoints().get(0).getX(), (int) curve.getInitialPoints().get(0).getY(),
                        (int) curve.getInitialPoints().get(curve.getInitialPoints().size() - 1).getX(), (int) curve.getInitialPoints().get(curve.getInitialPoints().size() - 1).getY());
            }


            /**
             * Draw the rest of curve. And those tiny dots x)
             */


            graphics2d.setPaint(getCurveColor());
            graphics2d.setStroke(new BasicStroke(1.0f));

            miniPoints.clear();
            for (int i = 0; i < curve.getPoints().size(); i++) {
                matematikapirma.Point point = curve.getPoints().get(i);
                if (getSurface().getShowDots() && i == 0) {
                    MiniPoint pp = new MiniPoint(point);
                    miniPoints.add(pp);
                    graphics2d.draw(pp);
                }
                if (i + 1 < curve.getPoints().size()) {
                    matematikapirma.Point point2 = curve.getPoints().get(i + 1);
                    graphics2d.drawLine((int) Math.round(point.getX()), (int) Math.round(point.getY()),
                            (int) Math.round(point2.getX()), (int) Math.round(point2.getY()));
                    if (getSurface().getShowDots()) {
                        MiniPoint pp = new MiniPoint(point);
                        miniPoints.add(pp);
                        graphics2d.draw(pp);
                    }
                }
            }
        }

        /**
         * Intersection points
         */
        graphics2d.setPaint(Color.RED);
        graphics2d.setStroke(new BasicStroke(2.5f));
        int size = 2;
        for(Point point : getSurface().getIntersectingPoints()){
            graphics2d.draw(new Ellipse2D.Double(point.getX() - size / 2, point.getY() - size / 2, size, size));
        }
        
        colorNum = 0;
    }
}
